Render this script with > sbatch ~/spinal_cord_paper/scripts/Seurat_Gg_NT_int_array.sh

load the data

the following workflow is based on: - [https://github.com/satijalab/seurat/issues/1500] Cell cycle regression - [https://github.com/satijalab/seurat/issues/1836] General workflow - [https://github.com/satijalab/seurat/issues/2590] Integrate all features (although used in the integration script)

library(Seurat)
library(dplyr)
library(Rtsne)
library(RColorBrewer)
library(tidyr)
library(ggplot2)
library(gridExtra)
library(patchwork)
library(cowplot)
library(ggdendro)
gnames <- modplots::gnames
# load the integrated seurat
my.int <- readRDS(file = \~/spinal_cord_paper/data/Gg_ctrl_integrated_060223.rds\)

my.int@project.name <- \Gg_ctrl_int\

my.int

Date: 07.03.23

inspect

We order the plot by nCount_RNA to prevent overplotting of the last sample (default order is the group.by ident).

# have a look at the PC and the previously calculated UMAP
PCAPlot(
  my.int,
  group.by = \orig.ident\,
  order = \nCount_RNA\
  )

DimPlot(
  my.int,
  reduction = \umap\,
  group.by = \orig.ident\,
  order = \nCount_RNA\
  )

cell cycle scoring

Now we can score the cell cycle stage, using Seurats function. For this wee need the ortholog of the distributed cell stage marker lists.

We do the CC.difference calculation at this stage - and not prior to integration - based on the suggestion from: [https://github.com/satijalab/seurat/issues/1500]

We add the scores of stages S and G2M, as well as the difference between them to the metadata with the names: S, G2M, CC.Difference

PCA plot of non-regressed data

# Load the orhtology table
ortho_gg_mm_v102 <- readRDS(\~/spinal_cord_paper/data/ortho_gg_mm_v102.rds\) 

colnames(ortho_gg_mm_v102) <- c(
  \GG_gene_ID\,
  \GG_gene_Name\,
  \MM_gene_ID\,
  \MM_gene_Name\,
  \ortho_conf\,
  \homolog_type\
)

# A list of cell cycle markers, from Tirosh et al, 2015, is loaded with Seurat.  We can
# segregate this list into markers of G2/M phase and markers of S phase
s.genes <- ortho_gg_mm_v102 %>%
  dplyr::mutate(MM_gene_Name = toupper(MM_gene_Name)) %>%
  dplyr::filter(MM_gene_Name %in% cc.genes$s.genes) %>%
  dplyr::arrange(match(MM_gene_Name, cc.genes$s.genes)) %>%
  dplyr::pull(GG_gene_ID)

g2m.genes <- ortho_gg_mm_v102 %>%
  dplyr::mutate(MM_gene_Name = toupper(MM_gene_Name)) %>%
  dplyr::filter(MM_gene_Name %in% cc.genes$g2m.genes) %>%
  dplyr::arrange(match(MM_gene_Name, cc.genes$g2m.genes)) %>%
  dplyr::pull(GG_gene_ID) 

t0 <- Sys.time()
my.int <- CellCycleScoring(
  my.int,
  s.features = s.genes,
  g2m.features = g2m.genes,
  set.ident = TRUE
  )
paste0(\Seurats CC scoring took \, Sys.time() - t0, \ seconds to run.\)

my.int$CC.Difference.seurat <- my.int$S.Score - my.int$G2M.Score
# view cell cycle scores and phase assignments
head(my.int[[]])

rm(t0, s.genes, g2m.genes)

rescale integrated assay

We rescale the data to regress out the CC.difference

all_genes <- rownames(my.int)

my.int <- ScaleData(
  my.int,
  features = all_genes,
  vars.to.regress = \CC.Difference.seurat\
)

PCA

PCAPlot(my.int, group.by = \Phase\)

Dim Reduction

We will run the tSNE. Using the FFT-accelerated Interpolation-based t-SNE (FIt-SNE). We run two, a normal tSNE, and an exaggerated tSNE, where clusters are tighter togheter. This is located under the xtsne name.

# Get the tSNE function
source(\~/Software/FIt-SNE-master/fast_tsne.R\)

my.tsne = fftRtsne(my.int@reductions$pca@cell.embeddings[,my.dimensions],
                   max_iter = 1000,
                   learning_rate =  round(dim(my.int)[2]/12),
                   initialization =(my.int@reductions$pca@cell.embeddings[,1:2]/my.int@reductions$pca@stdev[1])*0.0001,
                   perplexity_list = c(30, round(dim(my.int)[2]/100)),
                   fast_tsne_path=\~/Software/FIt-SNE-master/bin/fast_tsne\)

colnames(my.tsne) = c(\tsne_1\, \tsne_2\)
rownames(my.tsne) = colnames(my.int)

my.int[[\tsne\]] = CreateDimReducObject(embeddings = my.tsne, key = \tsne_\, assay = DefaultAssay(my.int), global = T)

Clustering

Here we perform the Louvain-jaccard clustering implemented in Seurat. We can see the tree of clusters, to see how the clusters relate in the PCA space.
We also use the tree, to check if any of the terminal pairs of sisters should be merged. This is determined based on a minimum of 20 DEs between the clusters.

# Find first the nearest neighbors
my.int <- FindNeighbors(object = my.int, dims=my.dimensions, verbose = F)

# Then the actual clusters
my.int <- FindClusters(object = my.int, resolution = 1, verbose = F, random.seed = 42)

# Check the tree of clusters, to see what's the relationship between them
my.int <- BuildClusterTree(my.int, dims = my.dimensions, verbose = F)
plot(Tool(object = my.int, slot = 'BuildClusterTree'))

# We are gonna check for DEs using the non integrated data. We are only gonna test genes that have variability, so we calculate variable genes
my.int <- FindVariableFeatures(my.int, assay = \RNA\)


# Only the genes with variability > median
my.HVF <- HVFInfo(my.int, assay = \RNA\)
my.HVF <- rownames(my.HVF)[which(my.HVF[,3] > (median(my.HVF[,3])))]
# We check for pairs of clusters, how mayn DEs they have. If less than n, we merge them
keep.check <- T
while (keep.check == T) {
  
  keep.check <- F
  # Check the tree of clusters, to see what's the relationship between them
  my.int <- BuildClusterTree(my.int, dims = my.dimensions, verbose = F)
  # Check only the terminal sisters
  to.check = ips::terminalSisters(my.int@tools$BuildClusterTree)
  for (i in to.check) {
    # DE between the sisters
    my.DE <- FindMarkers(my.int, i[1], i[2], test.use = \MAST\, latent.vars = c(\CC.Difference.seurat\),
                         min.pct = 0.25, verbose = T, assay = \RNA\, features = my.HVF,) %>%
      dplyr::filter(abs(avg_log2FC) > 0.5) %>%
      dplyr::filter(p_val_adj < 0.05)
    
    # If less than 5, merge, and repeat
    if (dim(my.DE)[1] < 5) {
      cat(paste0(dim(my.DE)[1], \ genes differentially expressed between clusters \,i[1],\ and \,i[2],\ merging \n\))
      my.int <- SetIdent(my.int, cells = WhichCells(my.int, idents = i[2]), value = i[1])
      keep.check <- T
    }
    print(i)
  }
}
rm(to.check, my.HVF, my.DE)
# renumber starting from 1
my.ID <- factor(
  Idents(my.int),
  levels= levels(Idents(my.int))[base::order(as.numeric(levels(Idents(my.int))))])
levels(my.ID) <- seq(length(levels(my.ID)))
Idents(my.int) <- my.ID
my.int[[\seurat_clusters\]] <- my.ID

# Check again the clusters
dim1 <- DimPlot(my.int, reduction = \tsne\, cols = rainbow(length(levels(my.ID))), label = T, label.size = 5, pt.size = 1)
dim1

Plot the tree again

# tree based on PCA dims
my.int <- BuildClusterTree(my.int, dims = my.dimensions, verbose = F)
plot(Tool(object = my.int, slot = 'BuildClusterTree'))

Now that we have the final reductions, we’ll choose one and we look at the statistics of the cells.

# Run the actual PCA (by default uses var.features of assay)
my.int <- RunPCA(my.int, verbose = F)

PCAPlot(my.int, group.by = \Phase\)

# Which dimensions will we choose?
hist(my.int@reductions$pca@stdev^2, breaks = 500)

my.dimensions=1:20
# set and get dim.reduct embeddings
my.reduc <- \tsne\
emb <- data.frame(Embeddings(my.int, my.reduc))
colnames(emb) <- c(\reduc_1\, \reduc_2\)

meta <- my.int[[]] %>%
  tibble::rownames_to_column(\cell_ID\)


my.plots = list()

my.plots[[1]] = ggplot(emb[meta[order(meta$nCount_RNA),]$cell_ID, ], aes(x = reduc_1, y = reduc_2)) +
    geom_point(aes(color=sort(meta$nCount_RNA)), size=1, alpha = 0.4, pch = 19) + 
    scale_colour_gradientn(colours = c(\gray90\,\gray90\,\gray80\,\yellow\, \orange\, \red\, \darkred\, \darkred\)) +
    theme_classic() + labs(colour=\UMI count\, x = paste0(my.reduc, \_1\), y = paste0(my.reduc, \_2\))

my.plots[[2]] = ggplot(emb[meta[order(meta$nFeature_RNA),]$cell_ID, ], aes(x = reduc_1, y = reduc_2)) +
    geom_point(aes(color=sort(meta$nFeature_RNA)), size=1, alpha = 0.4, pch = 19) + 
    scale_colour_gradientn(colours = c(\gray90\,\gray90\,\gray80\,\yellow\, \orange\, \red\, \darkred\, \darkred\)) +
    theme_classic() + labs(colour=\gene count\, x = paste0(my.reduc, \_1\), y = paste0(my.reduc, \_2\))

my.plots[[3]] = ggplot(emb[meta[order(meta$nCount_SCT),]$cell_ID, ], aes(x = reduc_1, y = reduc_2)) +
    geom_point(aes(color=sort(meta$nCount_RNA)), size=1, alpha = 0.4, pch = 19) + 
    scale_colour_gradientn(colours = c(\gray90\,\gray90\,\gray80\,\yellow\, \orange\, \red\, \darkred\, \darkred\)) +
    theme_classic() + labs(colour=\UMI count (SCT)\, x = paste0(my.reduc, \_1\), y = paste0(my.reduc, \_2\))

my.plots[[4]] = ggplot(emb[meta[order(meta$nFeature_SCT),]$cell_ID, ], aes(x = reduc_1, y = reduc_2)) +
    geom_point(aes(color=sort(meta$nFeature_RNA)), size=1, alpha = 0.4, pch = 19) + 
    scale_colour_gradientn(colours = c(\gray90\,\gray90\,\gray80\,\yellow\, \orange\, \red\, \darkred\, \darkred\)) +
    theme_classic() + labs(colour=\gene count (SCT)\, x = paste0(my.reduc, \_1\), y = paste0(my.reduc, \_2\))

my.plots[[5]] = ggplot(emb[meta[order(meta$percent.mt),]$cell_ID, ], aes(x = reduc_1, y = reduc_2)) +
    geom_point(aes(color=(sort(meta$percent.mt))), size=1, alpha = 0.4, pch = 19) + 
    scale_colour_gradientn(colours = c(\gray90\,\gray80\,\yellow\, \orange\, \red\, \darkred\, \darkred\)) +
    theme_classic() + labs(colour=\log1p MT percent\, x = paste0(my.reduc, \_1\), y = paste0(my.reduc, \_2\))

my.plots[[6]] = ggplot(emb[meta[order(meta$CC.Difference.seurat),]$cell_ID, ], aes(x = reduc_1, y = reduc_2)) +
    geom_point(aes(color=sort(meta$CC.Difference.seurat)), size=1, alpha = 0.4, pch = 19) + 
    scale_colour_gradientn(colours = c(\gray90\,\gray90\,\gray80\,\yellow\, \orange\, \red\, \darkred\, \darkred\)) +
    theme_classic() + labs(colour=\Cell Cycle\nS-G2M\, x = paste0(my.reduc, \_1\), y = paste0(my.reduc, \_2\))

my.plots[[7]] = ggplot(emb[meta[order(meta$percent.rb),]$cell_ID, ], aes(x = reduc_1, y = reduc_2)) +
    geom_point(aes(color=sort(meta$percent.rb)), size=2, alpha = 0.4, pch = 19) + 
    scale_colour_gradientn(colours = c(\gray90\,\gray90\,\gray80\,\yellow\, \orange\, \red\, \darkred\, \darkred\)) +
    theme_classic() + labs(colour=\percent.rb\)

grid.arrange(grobs=my.plots, ncol=2)

dim.orig.ident <- ggplot(emb, aes(x = reduc_1, y = reduc_2)) +
    geom_point(aes(color=meta$orig.ident[sample(x = seq(nrow(emb)), size = nrow(emb), replace = FALSE)]), size=0.8, alpha = 0.6, pch = 19) + 
    scale_colour_manual(values = rainbow(length(table(my.int@meta.data$orig.ident))) ) +
    theme_classic() + labs(colour=\Dataset\, x = paste0(my.reduc, \_1\), y = paste0(my.reduc, \_2\))

dim.orig.ident

DV domain plots

To identify the different DV domains of the neuron and progenitor clusters, we plot their specific markers.

neurons <- list(dI1 = c(\LHX2\,\LHX9\,\BARHL1\,\BARHL2\,\POU4F1\),
  dI2 = c(\LHX1\,\LHX5\,\POU4F1\),
  dI3 = c(\ISL1\,\TLX3\,\DRGX\,\POU4F1\),
  dI4 = c(\LBX1\,\PAX2\,\LHX1\,\LHX5\),
  dI5 = c(\LBX1\,\LMX1B\,\TLX3\,\DRGX\,\POU4F1\),
  dI6 = c(\LBX1\,\PAX2\,\LHX1\,\LHX5\),
  V0 = c(\EVX1\,\PAX2\,\LHX1\,\LHX5\),
  V1 = c(\EN1\,\PAX2\,\LHX1\,\LHX5\),
  V2a = c(\VSX1\,\SOX14\,\LHX3\),
  V2b = c(\GATA2\,\GATA3\,\TAL1\),
  MN =  c(\MNX1\,\ISL1\,\LHX3\,\ISL2\, \SLC18A3\))

prog <- list(dp1_3 =  c(\PAX6\,\IRX3\,\IRX5\,\MSX1\,\PAX3\,\PAX7\),
  dp4 = c(\PAX6\,\IRX3\,\IRX5\,\GSX1\,\PAX3\,\PAX7\),
  dp5 = c(\PAX6\,\IRX3\,\IRX5\,\DBX2\,\GSX1\,\PAX3\,\PAX7\),
  dp6 = c(\PAX6\,\IRX3\,\IRX5\,\DBX2\,\LEUTX\,\PAX3\,\PAX7\),
  p0 = c(\PAX6\,\IRX3\,\IRX5\,\DBX2\,\LEUTX\),
  p1 = c(\PAX6\,\IRX3\,\IRX5\,\DBX2\,\PRDM12\),
  p2 = c(\PAX6\,\IRX3\,\IRX5\,\FOXN4\,\NKX6-1\),
  pMN = c(\PAX6\,\OLIG2\,\NKX6-1\),
  p3 = c(\NKX2-8\,\NKX2-2\,\NKX6-1\))

# allows to use mFeaturePlot with lapply
feat_list_plot <- function(x) {
 plot <- modplots::mFeaturePlot(my.int, my.features = x,
                       gnames = gnames, size = 0.2, return = TRUE)
 return(plot)
}

tsne_dim <- TSNEPlot(
  my.int,
  reduction = \tsne\,
  cols = rainbow(length(levels(my.ID))),
  pt.size = 0.01,
  label.size = 1,
  label = TRUE
) +
  ggplot2::theme(legend.position = \none\)

plots_prog <- lapply(prog, feat_list_plot)
Not all features are present in your object! Removing:  IRX5 
Not all features are present in your object! Removing:  IRX5 
Not all features are present in your object! Removing:  IRX5 
Not all features are present in your object! Removing:  IRX5 
Not all features are present in your object! Removing:  IRX5 
Not all features are present in your object! Removing:  IRX5 
Not all features are present in your object! Removing:  IRX5 FOXN4 
plots_neur <- lapply(neurons, feat_list_plot)
Not all features are present in your object! Removing:  LHX2 LHX9 BARHL2 
Not all features are present in your object! Removing:  LBX1 
Not all features are present in your object! Removing:  LBX1 
Not all features are present in your object! Removing:  LBX1 
Not all features are present in your object! Removing:  MNX1 ISL2 
pdf(paste0(\~/spinal_cord_paper/figures/DV_prog_domain_\,my.int@project.name ,\.pdf\), width = 7, height = 5)
  for (j in names(prog)) {
    plots_prog[[j]][[\tsne\]] <- tsne_dim
    gridExtra::grid.arrange(grobs = plots_prog[[j]], ncol = 3, top = j)
  }
dev.off()
png 
  2 
pdf(paste0(\~/spinal_cord_paper/figures/DV_neur_domain_\,my.int@project.name ,\.pdf\), width = 7, height = 5)
  for (j in names(neurons)) {
    plots_neur[[j]][[\tsne\]] <- tsne_dim
    gridExtra::grid.arrange(grobs = plots_neur[[j]], ncol = 3, top = j)
  }
  dev.off()
png 
  2 

DE analysis

Here we do the differential expression analysis, and end up with the marker genes lists. We can also see the marker gene dot plot, for the top 2 marker genes per cluster

# Find all the marker genes, with these thresholds MAST
semarkers = FindAllMarkers(my.int,
                           features = my.int[[\integrated\]]@var.features, 
                           only.pos = TRUE, 
                           min.pct = 0.25, 
                           logfc.threshold =  0.5,
                           latent.vars = c(\CC.Difference.seurat\), 
                           test.use = \MAST\, 
                           assay = \RNA\, 
                           return.thresh = 0.05)

# We only keep the significant ones
semarkers <- semarkers %>%
  filter(p_val_adj < 0.05) %>% 
  rename(Gene.stable.ID = gene) %>% 
  left_join(gnames, by = \Gene.stable.ID\)


# Take only the top 10
semrk10 = semarkers %>% group_by(cluster) %>% top_n(-10, p_val_adj)
semrk1 = semarkers %>% group_by(cluster) %>% top_n(-1, p_val_adj)

modplots::mDotPlot2(my.int, 
          features = unique(semrk1$Gene.stable.ID), 
          cols = c(\grey\, \black\),  
          gnames = gnames, dot.scale = 6) +
  theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1))

DoHeatmap(my.int, semrk1$Gene.stable.ID)

save

saveRDS(my.int, paste0(\~/spinal_cord_paper/data/\, my.int@project.name, \_seurat_\,format(Sys.Date(), \%d%m%y\),\.rds\))

write.table(semarkers, sep = \\t\, row.names = T, col.names = T,
            file = paste0(\~/spinal_cord_paper/data/\, my.int@project.name, \_fullDE_\,format(Sys.Date(), \%d%m%y\),\.txt\), quote = F)

write.table(semrk10, sep = \\t\, row.names = T, col.names = T,
            file = paste0(\~/spinal_cord_paper/data/\, my.int@project.name, \_top10DE_\,format(Sys.Date(), \%d%m%y\),\.txt\), quote = F)
# Date and time of Rendering
Sys.time()
[1] \2023-02-07 11:12:54 CET\
sessionInfo()
R version 4.1.0 (2021-05-18)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: CentOS Linux 7 (Core)

Matrix products: default
BLAS/LAPACK: /scicore/soft/apps/OpenBLAS/0.3.1-GCC-7.3.0-2.30/lib/libopenblas_sandybridgep-r0.3.1.so

locale:
[1] en_US.UTF-8

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
 [1] ggdendro_0.1.22    cowplot_1.1.1      patchwork_1.1.1    gridExtra_2.3     
 [5] ggplot2_3.3.3      tidyr_1.1.3        RColorBrewer_1.1-2 Rtsne_0.15        
 [9] dplyr_1.0.10       SeuratObject_4.0.2 Seurat_4.0.5      

loaded via a namespace (and not attached):
  [1] fastmatch_1.1-0             plyr_1.8.6                 
  [3] igraph_1.2.6                lazyeval_0.2.2             
  [5] sp_1.4-5                    splines_4.1.0              
  [7] listenv_0.8.0               scattermore_0.7            
  [9] GenomeInfoDb_1.28.0         digest_0.6.27              
 [11] htmltools_0.5.1.1           ips_0.0.11                 
 [13] fansi_0.5.0                 magrittr_2.0.1             
 [15] memoise_2.0.0               tensor_1.5                 
 [17] cluster_2.1.2               ROCR_1.0-11                
 [19] globals_0.16.2              Biostrings_2.60.0          
 [21] matrixStats_0.58.0          modplots_1.0.0             
 [23] spatstat.sparse_3.0-0       prettyunits_1.1.1          
 [25] colorspace_2.0-1            blob_1.2.1                 
 [27] ggrepel_0.9.1               xfun_0.34                  
 [29] crayon_1.4.1                RCurl_1.98-1.3             
 [31] jsonlite_1.7.2              spatstat.data_3.0-0        
 [33] phangorn_2.7.0              ape_5.5                    
 [35] survival_3.2-11             zoo_1.8-9                  
 [37] glue_1.6.2                  polyclip_1.10-0            
 [39] gtable_0.3.0                zlibbioc_1.38.0            
 [41] XVector_0.32.0              leiden_0.3.9               
 [43] DelayedArray_0.18.0         SingleCellExperiment_1.14.1
 [45] future.apply_1.7.0          BiocGenerics_0.38.0        
 [47] abind_1.4-5                 scales_1.1.1               
 [49] pheatmap_1.0.12             DBI_1.1.1                  
 [51] miniUI_0.1.1.1              Rcpp_1.0.7                 
 [53] progress_1.2.2              viridisLite_0.4.0          
 [55] xtable_1.8-4                reticulate_1.22            
 [57] spatstat.core_2.1-2         bit_4.0.4                  
 [59] stats4_4.1.0                htmlwidgets_1.5.3          
 [61] httr_1.4.2                  ellipsis_0.3.2             
 [63] ica_1.0-2                   XML_3.99-0.6               
 [65] farver_2.1.0                pkgconfig_2.0.3            
 [67] sass_0.4.0                  uwot_0.1.10                
 [69] deldir_1.0-6                utf8_1.2.1                 
 [71] labeling_0.4.2              tidyselect_1.2.0           
 [73] rlang_1.0.6                 reshape2_1.4.4             
 [75] later_1.2.0                 AnnotationDbi_1.54.0       
 [77] munsell_0.5.0               tools_4.1.0                
 [79] cachem_1.0.5                cli_3.4.1                  
 [81] generics_0.1.3              RSQLite_2.2.7              
 [83] ggridges_0.5.3              org.Gg.eg.db_3.13.0        
 [85] evaluate_0.20               stringr_1.4.0              
 [87] fastmap_1.1.0               yaml_2.2.1                 
 [89] goftest_1.2-2               knitr_1.41                 
 [91] bit64_4.0.5                 fitdistrplus_1.1-6         
 [93] purrr_0.3.4                 RANN_2.6.1                 
 [95] KEGGREST_1.32.0             pbapply_1.4-3              
 [97] future_1.30.0               nlme_3.1-152               
 [99] mime_0.10                   compiler_4.1.0             
[101] plotly_4.10.0               png_0.1-7                  
[103] spatstat.utils_3.0-1        tibble_3.1.8               
[105] bslib_0.2.5.1               stringi_1.6.2              
[107] highr_0.9                   lattice_0.20-44            
[109] Matrix_1.3-3                vctrs_0.5.1                
[111] pillar_1.8.1                lifecycle_1.0.3            
[113] spatstat.geom_3.0-3         lmtest_0.9-38              
[115] jquerylib_0.1.4             RcppAnnoy_0.0.19           
[117] data.table_1.14.0           bitops_1.0-7               
[119] irlba_2.3.3                 GenomicRanges_1.44.0       
[121] httpuv_1.6.1                R6_2.5.0                   
[123] promises_1.2.0.1            KernSmooth_2.23-20         
[125] IRanges_2.26.0              parallelly_1.33.0          
[127] codetools_0.2-18            MASS_7.3-54                
[129] assertthat_0.2.1            MAST_1.18.0                
[131] SummarizedExperiment_1.22.0 withr_2.4.2                
[133] sctransform_0.3.3           S4Vectors_0.30.0           
[135] GenomeInfoDbData_1.2.6      hms_1.1.0                  
[137] mgcv_1.8-35                 parallel_4.1.0             
[139] quadprog_1.5-8              grid_4.1.0                 
[141] rpart_4.1-15                rmarkdown_2.17             
[143] MatrixGenerics_1.4.0        Biobase_2.52.0             
[145] shiny_1.6.0                
LS0tCnRpdGxlOiAiU2V1cmF0IHdvcmtmbG93IG9mIHRoZSBpbnRlZ3JhdGVkIEdnIGN0cmwgMSBhbmQgMiBkYXRhIgphdXRob3I6ICJGYWJpbyBTYWNoZXIiCmRhdGU6ICIwNi4wMi4yMDIzIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogVFJVRQogICAgdG9jX2Zsb2F0OiBUUlVFCiAgICBkZl9wcmludDogcGFnZWQKICBodG1sX25vdGVib29rOgogICAgZmlnX2hlaWdodDogNwogICAgZmlnX3dpZHRoOiA4CmVkaXRvcl9vcHRpb25zOgogIGNodW5rX291dHB1dF90eXBlOiBpbmxpbmUKLS0tCgpgYGB7ciwgc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldCgKICBjb21tZW50ID0gJycsIGZpZy53aWR0aCA9IDEwLCBmaWcuaGVpZ2h0ID0gNwopCmBgYAoKUmVuZGVyIHRoaXMgc2NyaXB0IHdpdGggPiBzYmF0Y2ggfi9zcGluYWxfY29yZF9wYXBlci9zY3JpcHRzL1NldXJhdF9HZ19OVF9pbnRfYXJyYXkuc2gKCiMgbG9hZCB0aGUgZGF0YQoKdGhlIGZvbGxvd2luZyB3b3JrZmxvdyBpcyBiYXNlZCBvbjoKIC0gW2h0dHBzOi8vZ2l0aHViLmNvbS9zYXRpamFsYWIvc2V1cmF0L2lzc3Vlcy8xNTAwXSBDZWxsIGN5Y2xlIHJlZ3Jlc3Npb24KIC0gW2h0dHBzOi8vZ2l0aHViLmNvbS9zYXRpamFsYWIvc2V1cmF0L2lzc3Vlcy8xODM2XSBHZW5lcmFsIHdvcmtmbG93CiAtIFtodHRwczovL2dpdGh1Yi5jb20vc2F0aWphbGFiL3NldXJhdC9pc3N1ZXMvMjU5MF0gSW50ZWdyYXRlIGFsbCBmZWF0dXJlcyAoYWx0aG91Z2ggdXNlZCBpbiB0aGUgaW50ZWdyYXRpb24gc2NyaXB0KQoKYGBge3IgbGlicmFyaWVzLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgZWNobyA9IFQsIHJlc3VsdHMgPSAnaGlkZSd9CmxpYnJhcnkoU2V1cmF0KQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KFJ0c25lKQpsaWJyYXJ5KFJDb2xvckJyZXdlcikKbGlicmFyeSh0aWR5cikKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGdyaWRFeHRyYSkKbGlicmFyeShwYXRjaHdvcmspCmxpYnJhcnkoY293cGxvdCkKbGlicmFyeShnZ2RlbmRybykKCmBgYAoKYGBge3IgaW5wdXQsIGVjaG8gPSBULCByZXN1bHRzID0gJ2hpZGUnLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQoKZ25hbWVzIDwtIG1vZHBsb3RzOjpnbmFtZXMKIyBsb2FkIHRoZSBpbnRlZ3JhdGVkIHNldXJhdApteS5pbnQgPC0gcmVhZFJEUyhmaWxlID0gIn4vc3BpbmFsX2NvcmRfcGFwZXIvZGF0YS9HZ19jdHJsX2ludGVncmF0ZWRfMDcwMjIzLnJkcyIpCgpteS5pbnRAcHJvamVjdC5uYW1lIDwtICJHZ19jdHJsX2ludCIKCm15LmludApgYGAKCkRhdGU6IGByIGZvcm1hdChTeXMuRGF0ZSgpLCAiJWQuJW0uJXkiKWAKCiMgaW5zcGVjdAoKV2Ugb3JkZXIgdGhlIHBsb3QgYnkgbkNvdW50X1JOQSB0byBwcmV2ZW50IG92ZXJwbG90dGluZyBvZiB0aGUgbGFzdCBzYW1wbGUgKGRlZmF1bHQgb3JkZXIgaXMgdGhlIGdyb3VwLmJ5IGlkZW50KS4KCmBgYHtyIGluc3BlY3Rpb259CiMgaGF2ZSBhIGxvb2sgYXQgdGhlIFBDIGFuZCB0aGUgcHJldmlvdXNseSBjYWxjdWxhdGVkIFVNQVAKUENBUGxvdCgKICBteS5pbnQsCiAgZ3JvdXAuYnkgPSAib3JpZy5pZGVudCIsCiAgb3JkZXIgPSAibkNvdW50X1JOQSIKICApCgpEaW1QbG90KAogIG15LmludCwKICByZWR1Y3Rpb24gPSAidW1hcCIsCiAgZ3JvdXAuYnkgPSAib3JpZy5pZGVudCIsCiAgb3JkZXIgPSAibkNvdW50X1JOQSIKICApCgpgYGAKCiMgY2VsbCBjeWNsZSBzY29yaW5nCgpOb3cgd2UgY2FuIHNjb3JlIHRoZSBjZWxsIGN5Y2xlIHN0YWdlLCB1c2luZyBTZXVyYXRzIGZ1bmN0aW9uLiBGb3IgdGhpcyB3ZWUgbmVlZCB0aGUgb3J0aG9sb2cgb2YgdGhlIGRpc3RyaWJ1dGVkIGNlbGwgc3RhZ2UgbWFya2VyIGxpc3RzLiAKCldlIGRvIHRoZSBDQy5kaWZmZXJlbmNlIGNhbGN1bGF0aW9uIGF0IHRoaXMgc3RhZ2UgLSBhbmQgbm90IHByaW9yIHRvIGludGVncmF0aW9uIC0gYmFzZWQgb24gdGhlIHN1Z2dlc3Rpb24gZnJvbTogW2h0dHBzOi8vZ2l0aHViLmNvbS9zYXRpamFsYWIvc2V1cmF0L2lzc3Vlcy8xNTAwXQoKV2UgYWRkIHRoZSBzY29yZXMgb2Ygc3RhZ2VzIFMgYW5kIEcyTSwgYXMgd2VsbCBhcyB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZW0gdG8gdGhlIG1ldGFkYXRhIHdpdGggdGhlIG5hbWVzOiBTLCBHMk0sIENDLkRpZmZlcmVuY2UKCmBgYHtyICwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZWNobyA9IFQsIHJlc3VsdHMgPSAnaGlkZSd9CiMgTG9hZCB0aGUgb3JodG9sb2d5IHRhYmxlCm9ydGhvX2dnX21tX3YxMDIgPC0gcmVhZFJEUygifi9zcGluYWxfY29yZF9wYXBlci9kYXRhL29ydGhvX2dnX21tX3YxMDIucmRzIikgCgpjb2xuYW1lcyhvcnRob19nZ19tbV92MTAyKSA8LSBjKAogICJHR19nZW5lX0lEIiwKICAiR0dfZ2VuZV9OYW1lIiwKICAiTU1fZ2VuZV9JRCIsCiAgIk1NX2dlbmVfTmFtZSIsCiAgIm9ydGhvX2NvbmYiLAogICJob21vbG9nX3R5cGUiCikKCiMgQSBsaXN0IG9mIGNlbGwgY3ljbGUgbWFya2VycywgZnJvbSBUaXJvc2ggZXQgYWwsIDIwMTUsIGlzIGxvYWRlZCB3aXRoIFNldXJhdC4gIFdlIGNhbgojIHNlZ3JlZ2F0ZSB0aGlzIGxpc3QgaW50byBtYXJrZXJzIG9mIEcyL00gcGhhc2UgYW5kIG1hcmtlcnMgb2YgUyBwaGFzZQpzLmdlbmVzIDwtIG9ydGhvX2dnX21tX3YxMDIgJT4lCiAgZHBseXI6Om11dGF0ZShNTV9nZW5lX05hbWUgPSB0b3VwcGVyKE1NX2dlbmVfTmFtZSkpICU+JQogIGRwbHlyOjpmaWx0ZXIoTU1fZ2VuZV9OYW1lICVpbiUgY2MuZ2VuZXMkcy5nZW5lcykgJT4lCiAgZHBseXI6OmFycmFuZ2UobWF0Y2goTU1fZ2VuZV9OYW1lLCBjYy5nZW5lcyRzLmdlbmVzKSkgJT4lCiAgZHBseXI6OnB1bGwoR0dfZ2VuZV9JRCkKCmcybS5nZW5lcyA8LSBvcnRob19nZ19tbV92MTAyICU+JQogIGRwbHlyOjptdXRhdGUoTU1fZ2VuZV9OYW1lID0gdG91cHBlcihNTV9nZW5lX05hbWUpKSAlPiUKICBkcGx5cjo6ZmlsdGVyKE1NX2dlbmVfTmFtZSAlaW4lIGNjLmdlbmVzJGcybS5nZW5lcykgJT4lCiAgZHBseXI6OmFycmFuZ2UobWF0Y2goTU1fZ2VuZV9OYW1lLCBjYy5nZW5lcyRnMm0uZ2VuZXMpKSAlPiUKICBkcGx5cjo6cHVsbChHR19nZW5lX0lEKSAKCnQwIDwtIFN5cy50aW1lKCkKbXkuaW50IDwtIENlbGxDeWNsZVNjb3JpbmcoCiAgbXkuaW50LAogIHMuZmVhdHVyZXMgPSBzLmdlbmVzLAogIGcybS5mZWF0dXJlcyA9IGcybS5nZW5lcywKICBzZXQuaWRlbnQgPSBUUlVFCiAgKQpwYXN0ZTAoIlNldXJhdHMgQ0Mgc2NvcmluZyB0b29rICIsIFN5cy50aW1lKCkgLSB0MCwgIiBzZWNvbmRzIHRvIHJ1bi4iKQoKbXkuaW50JENDLkRpZmZlcmVuY2Uuc2V1cmF0IDwtIG15LmludCRTLlNjb3JlIC0gbXkuaW50JEcyTS5TY29yZQojIHZpZXcgY2VsbCBjeWNsZSBzY29yZXMgYW5kIHBoYXNlIGFzc2lnbm1lbnRzCmhlYWQobXkuaW50W1tdXSkKCnJtKHQwLCBzLmdlbmVzLCBnMm0uZ2VuZXMpCmBgYAoKIyMgUENBIHBsb3Qgb2Ygbm9uLXJlZ3Jlc3NlZCBkYXRhCmBgYHtyfQpQQ0FQbG90KG15LmludCwgZ3JvdXAuYnkgPSAiUGhhc2UiKQpgYGAKCgojIHJlc2NhbGUgaW50ZWdyYXRlZCBhc3NheQoKV2UgcmVzY2FsZSB0aGUgZGF0YSB0byByZWdyZXNzIG91dCB0aGUgQ0MuZGlmZmVyZW5jZQoKYGBge3Igc2NhbGUuZGF0YS1yZWdyZXNzLmNjLmRpZmZ9CmFsbF9nZW5lcyA8LSByb3duYW1lcyhteS5pbnQpCgpteS5pbnQgPC0gU2NhbGVEYXRhKAogIG15LmludCwKICBmZWF0dXJlcyA9IGFsbF9nZW5lcywKICB2YXJzLnRvLnJlZ3Jlc3MgPSAiQ0MuRGlmZmVyZW5jZS5zZXVyYXQiCikKYGBgCgojIFBDQQoKYGBge3J9CiMgUnVuIHRoZSBhY3R1YWwgUENBIChieSBkZWZhdWx0IHVzZXMgdmFyLmZlYXR1cmVzIG9mIGFzc2F5KQpteS5pbnQgPC0gUnVuUENBKG15LmludCwgdmVyYm9zZSA9IEYpCgpQQ0FQbG90KG15LmludCwgZ3JvdXAuYnkgPSAiUGhhc2UiKQoKIyBXaGljaCBkaW1lbnNpb25zIHdpbGwgd2UgY2hvb3NlPwpoaXN0KG15LmludEByZWR1Y3Rpb25zJHBjYUBzdGRldl4yLCBicmVha3MgPSA1MDApCgpteS5kaW1lbnNpb25zPTE6MjAKCmBgYAoKIyBEaW0gUmVkdWN0aW9uCgpXZSB3aWxsIHJ1biB0aGUgdFNORS4gVXNpbmcgdGhlIEZGVC1hY2NlbGVyYXRlZCBJbnRlcnBvbGF0aW9uLWJhc2VkIHQtU05FIChGSXQtU05FKS4KV2UgcnVuIHR3bywgYSBub3JtYWwgdFNORSwgYW5kIGFuIGV4YWdnZXJhdGVkIHRTTkUsIHdoZXJlIGNsdXN0ZXJzIGFyZSB0aWdodGVyIHRvZ2hldGVyLiBUaGlzIGlzIGxvY2F0ZWQgdW5kZXIgdGhlIHh0c25lIG5hbWUuCgpgYGB7ciB0c25lLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBlY2hvID0gVCwgcmVzdWx0cyA9ICdoaWRlJ30KCiMgR2V0IHRoZSB0U05FIGZ1bmN0aW9uCnNvdXJjZSgifi9Tb2Z0d2FyZS9GSXQtU05FLW1hc3Rlci9mYXN0X3RzbmUuUiIpCgpteS50c25lID0gZmZ0UnRzbmUobXkuaW50QHJlZHVjdGlvbnMkcGNhQGNlbGwuZW1iZWRkaW5nc1ssbXkuZGltZW5zaW9uc10sCiAgICAgICAgICAgICAgICAgICBtYXhfaXRlciA9IDE1MDAsCiAgICAgICAgICAgICAgICAgICBsZWFybmluZ19yYXRlID0gIHJvdW5kKGRpbShteS5pbnQpWzJdLzEyKSwKICAgICAgICAgICAgICAgICAgIGluaXRpYWxpemF0aW9uID0obXkuaW50QHJlZHVjdGlvbnMkcGNhQGNlbGwuZW1iZWRkaW5nc1ssMToyXS9teS5pbnRAcmVkdWN0aW9ucyRwY2FAc3RkZXZbMV0pKjAuMDAwMSwKICAgICAgICAgICAgICAgICAgIHBlcnBsZXhpdHlfbGlzdCA9IGMoMjUsIHJvdW5kKGRpbShteS5pbnQpWzJdLzEwMCkpLAogICAgICAgICAgICAgICAgICAgZmFzdF90c25lX3BhdGg9In4vU29mdHdhcmUvRkl0LVNORS1tYXN0ZXIvYmluL2Zhc3RfdHNuZSIpCgpjb2xuYW1lcyhteS50c25lKSA9IGMoInRzbmVfMSIsICJ0c25lXzIiKQpyb3duYW1lcyhteS50c25lKSA9IGNvbG5hbWVzKG15LmludCkKCm15LmludFtbInRzbmUiXV0gPSBDcmVhdGVEaW1SZWR1Y09iamVjdChlbWJlZGRpbmdzID0gbXkudHNuZSwga2V5ID0gInRzbmVfIiwgYXNzYXkgPSBEZWZhdWx0QXNzYXkobXkuaW50KSwgZ2xvYmFsID0gVCkKCmBgYAoKIyBDbHVzdGVyaW5nCgpIZXJlIHdlIHBlcmZvcm0gdGhlIExvdXZhaW4tamFjY2FyZCBjbHVzdGVyaW5nIGltcGxlbWVudGVkIGluIFNldXJhdC4KV2UgY2FuIHNlZSB0aGUgdHJlZSBvZiBjbHVzdGVycywgdG8gc2VlIGhvdyB0aGUgY2x1c3RlcnMgcmVsYXRlIGluIHRoZSBQQ0Egc3BhY2UuICAgCldlIGFsc28gdXNlIHRoZSB0cmVlLCB0byBjaGVjayBpZiBhbnkgb2YgdGhlIHRlcm1pbmFsIHBhaXJzIG9mIHNpc3RlcnMgc2hvdWxkIGJlIG1lcmdlZC4gVGhpcyBpcyBkZXRlcm1pbmVkIGJhc2VkIG9uIGEgbWluaW11bSBvZiAyMCBERXMgYmV0d2VlbiB0aGUgY2x1c3RlcnMuCgpgYGB7ciBjbHVzdGVycywgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZWNobyA9IFQsIHJlc3VsdHMgPSAnaGlkZSd9CgojIEZpbmQgZmlyc3QgdGhlIG5lYXJlc3QgbmVpZ2hib3JzCm15LmludCA8LSBGaW5kTmVpZ2hib3JzKG9iamVjdCA9IG15LmludCwgZGltcz1teS5kaW1lbnNpb25zLCB2ZXJib3NlID0gRikKCiMgVGhlbiB0aGUgYWN0dWFsIGNsdXN0ZXJzCm15LmludCA8LSBGaW5kQ2x1c3RlcnMob2JqZWN0ID0gbXkuaW50LCByZXNvbHV0aW9uID0gMSwgdmVyYm9zZSA9IEYsIHJhbmRvbS5zZWVkID0gNDIpCgojIENoZWNrIHRoZSB0cmVlIG9mIGNsdXN0ZXJzLCB0byBzZWUgd2hhdCdzIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGVtCm15LmludCA8LSBCdWlsZENsdXN0ZXJUcmVlKG15LmludCwgZGltcyA9IG15LmRpbWVuc2lvbnMsIHZlcmJvc2UgPSBGKQpwbG90KFRvb2wob2JqZWN0ID0gbXkuaW50LCBzbG90ID0gJ0J1aWxkQ2x1c3RlclRyZWUnKSkKCiMgV2UgYXJlIGdvbm5hIGNoZWNrIGZvciBERXMgdXNpbmcgdGhlIG5vbiBpbnRlZ3JhdGVkIGRhdGEuIFdlIGFyZSBvbmx5IGdvbm5hIHRlc3QgZ2VuZXMgdGhhdCBoYXZlIHZhcmlhYmlsaXR5LCBzbyB3ZSBjYWxjdWxhdGUgdmFyaWFibGUgZ2VuZXMKbXkuaW50IDwtIEZpbmRWYXJpYWJsZUZlYXR1cmVzKG15LmludCwgYXNzYXkgPSAiUk5BIikKCgojIE9ubHkgdGhlIGdlbmVzIHdpdGggdmFyaWFiaWxpdHkgPiBtZWRpYW4KbXkuSFZGIDwtIEhWRkluZm8obXkuaW50LCBhc3NheSA9ICJSTkEiKQpteS5IVkYgPC0gcm93bmFtZXMobXkuSFZGKVt3aGljaChteS5IVkZbLDNdID4gKG1lZGlhbihteS5IVkZbLDNdKSkpXQojIFdlIGNoZWNrIGZvciBwYWlycyBvZiBjbHVzdGVycywgaG93IG1heW4gREVzIHRoZXkgaGF2ZS4gSWYgbGVzcyB0aGFuIG4sIHdlIG1lcmdlIHRoZW0Ka2VlcC5jaGVjayA8LSBUCndoaWxlIChrZWVwLmNoZWNrID09IFQpIHsKICAKICBrZWVwLmNoZWNrIDwtIEYKICAjIENoZWNrIHRoZSB0cmVlIG9mIGNsdXN0ZXJzLCB0byBzZWUgd2hhdCdzIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGVtCiAgbXkuaW50IDwtIEJ1aWxkQ2x1c3RlclRyZWUobXkuaW50LCBkaW1zID0gbXkuZGltZW5zaW9ucywgdmVyYm9zZSA9IEYpCiAgIyBDaGVjayBvbmx5IHRoZSB0ZXJtaW5hbCBzaXN0ZXJzCiAgdG8uY2hlY2sgPSBpcHM6OnRlcm1pbmFsU2lzdGVycyhteS5pbnRAdG9vbHMkQnVpbGRDbHVzdGVyVHJlZSkKICBmb3IgKGkgaW4gdG8uY2hlY2spIHsKICAgICMgREUgYmV0d2VlbiB0aGUgc2lzdGVycwogICAgbXkuREUgPC0gRmluZE1hcmtlcnMobXkuaW50LCBpWzFdLCBpWzJdLCB0ZXN0LnVzZSA9ICJNQVNUIiwgbGF0ZW50LnZhcnMgPSBjKCJDQy5EaWZmZXJlbmNlLnNldXJhdCIpLAogICAgICAgICAgICAgICAgICAgICAgICAgbWluLnBjdCA9IDAuMjUsIHZlcmJvc2UgPSBULCBhc3NheSA9ICJSTkEiLCBmZWF0dXJlcyA9IG15LkhWRiwpICU+JQogICAgICBkcGx5cjo6ZmlsdGVyKGFicyhhdmdfbG9nMkZDKSA+IDAuNSkgJT4lCiAgICAgIGRwbHlyOjpmaWx0ZXIocF92YWxfYWRqIDwgMC4wNSkKICAgIAogICAgIyBJZiBsZXNzIHRoYW4gNSwgbWVyZ2UsIGFuZCByZXBlYXQKICAgIGlmIChkaW0obXkuREUpWzFdIDwgNSkgewogICAgICBjYXQocGFzdGUwKGRpbShteS5ERSlbMV0sICIgZ2VuZXMgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGJldHdlZW4gY2x1c3RlcnMgIixpWzFdLCIgYW5kICIsaVsyXSwiIG1lcmdpbmcgXG4iKSkKICAgICAgbXkuaW50IDwtIFNldElkZW50KG15LmludCwgY2VsbHMgPSBXaGljaENlbGxzKG15LmludCwgaWRlbnRzID0gaVsyXSksIHZhbHVlID0gaVsxXSkKICAgICAga2VlcC5jaGVjayA8LSBUCiAgICB9CiAgICBwcmludChpKQogIH0KfQpybSh0by5jaGVjaywgbXkuSFZGLCBteS5ERSkKCmBgYAoKYGBge3IgdHNuZV9hZ2FpbiwgZmlnLmhlaWdodCA9IDEyLCBmaWcud2lkdGggPSAxMn0KCiMgcmVudW1iZXIgc3RhcnRpbmcgZnJvbSAxCm15LklEIDwtIGZhY3RvcigKICBJZGVudHMobXkuaW50KSwKICBsZXZlbHM9IGxldmVscyhJZGVudHMobXkuaW50KSlbYmFzZTo6b3JkZXIoYXMubnVtZXJpYyhsZXZlbHMoSWRlbnRzKG15LmludCkpKSldKQpsZXZlbHMobXkuSUQpIDwtIHNlcShsZW5ndGgobGV2ZWxzKG15LklEKSkpCklkZW50cyhteS5pbnQpIDwtIG15LklECm15LmludFtbInNldXJhdF9jbHVzdGVycyJdXSA8LSBteS5JRAoKIyBDaGVjayBhZ2FpbiB0aGUgY2x1c3RlcnMKZGltMSA8LSBEaW1QbG90KG15LmludCwgcmVkdWN0aW9uID0gInRzbmUiLCBjb2xzID0gcmFpbmJvdyhsZW5ndGgobGV2ZWxzKG15LklEKSkpLCBsYWJlbCA9IFQsIGxhYmVsLnNpemUgPSA1LCBwdC5zaXplID0gMSkKZGltMQpgYGAKClBsb3QgdGhlIHRyZWUgYWdhaW4KCmBgYHtyIHJlbnVtYmVyZWQgdHJlZX0KIyB0cmVlIGJhc2VkIG9uIFBDQSBkaW1zCm15LmludCA8LSBCdWlsZENsdXN0ZXJUcmVlKG15LmludCwgZGltcyA9IG15LmRpbWVuc2lvbnMsIHZlcmJvc2UgPSBGKQpwbG90KFRvb2wob2JqZWN0ID0gbXkuaW50LCBzbG90ID0gJ0J1aWxkQ2x1c3RlclRyZWUnKSkKYGBgCgpOb3cgdGhhdCB3ZSBoYXZlIHRoZSBmaW5hbCByZWR1Y3Rpb25zLCB3ZSdsbCBjaG9vc2Ugb25lIGFuZCB3ZSBsb29rIGF0IHRoZSBzdGF0aXN0aWNzIG9mIHRoZSBjZWxscy4gCgpgYGB7cn0KbW9kcGxvdHM6Om1GZWF0dXJlUGxvdChteS5pbnQsIG15LmZlYXR1cmVzID0gYygiT0xJRzIiLCAiU09YOSIsICJUVUJCMyIsICJTSEgiLCAiU0xDMThBMyIsICJHQVRBMiIsICJQQVgyIiwgIlRMWDMiLCAiSUdGQlA3IiwgIkhCQkEiLCAiSUZJMzAiLCAiTVNYMiIsICJQTFAxIiksIGduYW1lcyA9IGduYW1lcykKYGBgCgpgYGB7ciBxcGxvdHMsIGZpZy5oZWlnaHQgPSAxNSwgZmlnLndpZHRoID0gMTAsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGVjaG8gPSBUfQoKIyBzZXQgYW5kIGdldCBkaW0ucmVkdWN0IGVtYmVkZGluZ3MKbXkucmVkdWMgPC0gInRzbmUiCmVtYiA8LSBkYXRhLmZyYW1lKEVtYmVkZGluZ3MobXkuaW50LCBteS5yZWR1YykpCmNvbG5hbWVzKGVtYikgPC0gYygicmVkdWNfMSIsICJyZWR1Y18yIikKCm1ldGEgPC0gbXkuaW50W1tdXSAlPiUKICB0aWJibGU6OnJvd25hbWVzX3RvX2NvbHVtbigiY2VsbF9JRCIpCgoKbXkucGxvdHMgPSBsaXN0KCkKCm15LnBsb3RzW1sxXV0gPSBnZ3Bsb3QoZW1iW21ldGFbb3JkZXIobWV0YSRuQ291bnRfUk5BKSxdJGNlbGxfSUQsIF0sIGFlcyh4ID0gcmVkdWNfMSwgeSA9IHJlZHVjXzIpKSArCiAgICBnZW9tX3BvaW50KGFlcyhjb2xvcj1zb3J0KG1ldGEkbkNvdW50X1JOQSkpLCBzaXplPTEsIGFscGhhID0gMC40LCBwY2ggPSAxOSkgKyAKICAgIHNjYWxlX2NvbG91cl9ncmFkaWVudG4oY29sb3VycyA9IGMoImdyYXk5MCIsImdyYXk5MCIsImdyYXk4MCIsInllbGxvdyIsICJvcmFuZ2UiLCAicmVkIiwgImRhcmtyZWQiLCAiZGFya3JlZCIpKSArCiAgICB0aGVtZV9jbGFzc2ljKCkgKyBsYWJzKGNvbG91cj0iVU1JIGNvdW50IiwgeCA9IHBhc3RlMChteS5yZWR1YywgIl8xIiksIHkgPSBwYXN0ZTAobXkucmVkdWMsICJfMiIpKQoKbXkucGxvdHNbWzJdXSA9IGdncGxvdChlbWJbbWV0YVtvcmRlcihtZXRhJG5GZWF0dXJlX1JOQSksXSRjZWxsX0lELCBdLCBhZXMoeCA9IHJlZHVjXzEsIHkgPSByZWR1Y18yKSkgKwogICAgZ2VvbV9wb2ludChhZXMoY29sb3I9c29ydChtZXRhJG5GZWF0dXJlX1JOQSkpLCBzaXplPTEsIGFscGhhID0gMC40LCBwY2ggPSAxOSkgKyAKICAgIHNjYWxlX2NvbG91cl9ncmFkaWVudG4oY29sb3VycyA9IGMoImdyYXk5MCIsImdyYXk5MCIsImdyYXk4MCIsInllbGxvdyIsICJvcmFuZ2UiLCAicmVkIiwgImRhcmtyZWQiLCAiZGFya3JlZCIpKSArCiAgICB0aGVtZV9jbGFzc2ljKCkgKyBsYWJzKGNvbG91cj0iZ2VuZSBjb3VudCIsIHggPSBwYXN0ZTAobXkucmVkdWMsICJfMSIpLCB5ID0gcGFzdGUwKG15LnJlZHVjLCAiXzIiKSkKCm15LnBsb3RzW1szXV0gPSBnZ3Bsb3QoZW1iW21ldGFbb3JkZXIobWV0YSRuQ291bnRfU0NUKSxdJGNlbGxfSUQsIF0sIGFlcyh4ID0gcmVkdWNfMSwgeSA9IHJlZHVjXzIpKSArCiAgICBnZW9tX3BvaW50KGFlcyhjb2xvcj1zb3J0KG1ldGEkbkNvdW50X1JOQSkpLCBzaXplPTEsIGFscGhhID0gMC40LCBwY2ggPSAxOSkgKyAKICAgIHNjYWxlX2NvbG91cl9ncmFkaWVudG4oY29sb3VycyA9IGMoImdyYXk5MCIsImdyYXk5MCIsImdyYXk4MCIsInllbGxvdyIsICJvcmFuZ2UiLCAicmVkIiwgImRhcmtyZWQiLCAiZGFya3JlZCIpKSArCiAgICB0aGVtZV9jbGFzc2ljKCkgKyBsYWJzKGNvbG91cj0iVU1JIGNvdW50IChTQ1QpIiwgeCA9IHBhc3RlMChteS5yZWR1YywgIl8xIiksIHkgPSBwYXN0ZTAobXkucmVkdWMsICJfMiIpKQoKbXkucGxvdHNbWzRdXSA9IGdncGxvdChlbWJbbWV0YVtvcmRlcihtZXRhJG5GZWF0dXJlX1NDVCksXSRjZWxsX0lELCBdLCBhZXMoeCA9IHJlZHVjXzEsIHkgPSByZWR1Y18yKSkgKwogICAgZ2VvbV9wb2ludChhZXMoY29sb3I9c29ydChtZXRhJG5GZWF0dXJlX1JOQSkpLCBzaXplPTEsIGFscGhhID0gMC40LCBwY2ggPSAxOSkgKyAKICAgIHNjYWxlX2NvbG91cl9ncmFkaWVudG4oY29sb3VycyA9IGMoImdyYXk5MCIsImdyYXk5MCIsImdyYXk4MCIsInllbGxvdyIsICJvcmFuZ2UiLCAicmVkIiwgImRhcmtyZWQiLCAiZGFya3JlZCIpKSArCiAgICB0aGVtZV9jbGFzc2ljKCkgKyBsYWJzKGNvbG91cj0iZ2VuZSBjb3VudCAoU0NUKSIsIHggPSBwYXN0ZTAobXkucmVkdWMsICJfMSIpLCB5ID0gcGFzdGUwKG15LnJlZHVjLCAiXzIiKSkKCm15LnBsb3RzW1s1XV0gPSBnZ3Bsb3QoZW1iW21ldGFbb3JkZXIobWV0YSRwZXJjZW50Lm10KSxdJGNlbGxfSUQsIF0sIGFlcyh4ID0gcmVkdWNfMSwgeSA9IHJlZHVjXzIpKSArCiAgICBnZW9tX3BvaW50KGFlcyhjb2xvcj0oc29ydChtZXRhJHBlcmNlbnQubXQpKSksIHNpemU9MSwgYWxwaGEgPSAwLjQsIHBjaCA9IDE5KSArIAogICAgc2NhbGVfY29sb3VyX2dyYWRpZW50bihjb2xvdXJzID0gYygiZ3JheTkwIiwiZ3JheTgwIiwieWVsbG93IiwgIm9yYW5nZSIsICJyZWQiLCAiZGFya3JlZCIsICJkYXJrcmVkIikpICsKICAgIHRoZW1lX2NsYXNzaWMoKSArIGxhYnMoY29sb3VyPSJsb2cxcCBNVCBwZXJjZW50IiwgeCA9IHBhc3RlMChteS5yZWR1YywgIl8xIiksIHkgPSBwYXN0ZTAobXkucmVkdWMsICJfMiIpKQoKbXkucGxvdHNbWzZdXSA9IGdncGxvdChlbWJbbWV0YVtvcmRlcihtZXRhJENDLkRpZmZlcmVuY2Uuc2V1cmF0KSxdJGNlbGxfSUQsIF0sIGFlcyh4ID0gcmVkdWNfMSwgeSA9IHJlZHVjXzIpKSArCiAgICBnZW9tX3BvaW50KGFlcyhjb2xvcj1zb3J0KG1ldGEkQ0MuRGlmZmVyZW5jZS5zZXVyYXQpKSwgc2l6ZT0xLCBhbHBoYSA9IDAuNCwgcGNoID0gMTkpICsgCiAgICBzY2FsZV9jb2xvdXJfZ3JhZGllbnRuKGNvbG91cnMgPSBjKCJncmF5OTAiLCJncmF5OTAiLCJncmF5ODAiLCJ5ZWxsb3ciLCAib3JhbmdlIiwgInJlZCIsICJkYXJrcmVkIiwgImRhcmtyZWQiKSkgKwogICAgdGhlbWVfY2xhc3NpYygpICsgbGFicyhjb2xvdXI9IkNlbGwgQ3ljbGVcblMtRzJNIiwgeCA9IHBhc3RlMChteS5yZWR1YywgIl8xIiksIHkgPSBwYXN0ZTAobXkucmVkdWMsICJfMiIpKQoKbXkucGxvdHNbWzddXSA9IGdncGxvdChlbWJbbWV0YVtvcmRlcihtZXRhJHBlcmNlbnQucmIpLF0kY2VsbF9JRCwgXSwgYWVzKHggPSByZWR1Y18xLCB5ID0gcmVkdWNfMikpICsKICAgIGdlb21fcG9pbnQoYWVzKGNvbG9yPXNvcnQobWV0YSRwZXJjZW50LnJiKSksIHNpemU9MiwgYWxwaGEgPSAwLjQsIHBjaCA9IDE5KSArIAogICAgc2NhbGVfY29sb3VyX2dyYWRpZW50bihjb2xvdXJzID0gYygiZ3JheTkwIiwiZ3JheTkwIiwiZ3JheTgwIiwieWVsbG93IiwgIm9yYW5nZSIsICJyZWQiLCAiZGFya3JlZCIsICJkYXJrcmVkIikpICsKICAgIHRoZW1lX2NsYXNzaWMoKSArIGxhYnMoY29sb3VyPSJwZXJjZW50LnJiIikKCmdyaWQuYXJyYW5nZShncm9icz1teS5wbG90cywgbmNvbD0yKQoKYGBgCgoKYGBge3Igb3JpZy5pZGVudC1yZWQucGxvdCwgZmlnLmhlaWdodCA9IDEwLCBmaWcud2lkdGggPSAxNX0KCmRpbS5vcmlnLmlkZW50IDwtIGdncGxvdChlbWIsIGFlcyh4ID0gcmVkdWNfMSwgeSA9IHJlZHVjXzIpKSArCiAgICBnZW9tX3BvaW50KGFlcyhjb2xvcj1tZXRhJG9yaWcuaWRlbnRbc2FtcGxlKHggPSBzZXEobnJvdyhlbWIpKSwgc2l6ZSA9IG5yb3coZW1iKSwgcmVwbGFjZSA9IEZBTFNFKV0pLCBzaXplPTAuOCwgYWxwaGEgPSAwLjYsIHBjaCA9IDE5KSArIAogICAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSByYWluYm93KGxlbmd0aCh0YWJsZShteS5pbnRAbWV0YS5kYXRhJG9yaWcuaWRlbnQpKSkgKSArCiAgICB0aGVtZV9jbGFzc2ljKCkgKyBsYWJzKGNvbG91cj0iRGF0YXNldCIsIHggPSBwYXN0ZTAobXkucmVkdWMsICJfMSIpLCB5ID0gcGFzdGUwKG15LnJlZHVjLCAiXzIiKSkKCmRpbS5vcmlnLmlkZW50CmBgYAoKIyBEViBkb21haW4gcGxvdHMKClRvIGlkZW50aWZ5IHRoZSBkaWZmZXJlbnQgRFYgZG9tYWlucyBvZiB0aGUgbmV1cm9uIGFuZCBwcm9nZW5pdG9yIGNsdXN0ZXJzLCB3ZSBwbG90IHRoZWlyIHNwZWNpZmljIG1hcmtlcnMuCgpgYGB7ciBEVi1kb219Cm5ldXJvbnMgPC0gbGlzdChkSTEgPSBjKCJMSFgyIiwiTEhYOSIsIkJBUkhMMSIsIkJBUkhMMiIsIlBPVTRGMSIpLAogIGRJMiA9IGMoIkxIWDEiLCJMSFg1IiwiUE9VNEYxIiksCiAgZEkzID0gYygiSVNMMSIsIlRMWDMiLCJEUkdYIiwiUE9VNEYxIiksCiAgZEk0ID0gYygiTEJYMSIsIlBBWDIiLCJMSFgxIiwiTEhYNSIpLAogIGRJNSA9IGMoIkxCWDEiLCJMTVgxQiIsIlRMWDMiLCJEUkdYIiwiUE9VNEYxIiksCiAgZEk2ID0gYygiTEJYMSIsIlBBWDIiLCJMSFgxIiwiTEhYNSIpLAogIFYwID0gYygiRVZYMSIsIlBBWDIiLCJMSFgxIiwiTEhYNSIpLAogIFYxID0gYygiRU4xIiwiUEFYMiIsIkxIWDEiLCJMSFg1IiksCiAgVjJhID0gYygiVlNYMSIsIlNPWDE0IiwiTEhYMyIpLAogIFYyYiA9IGMoIkdBVEEyIiwiR0FUQTMiLCJUQUwxIiksCiAgTU4gPSAgYygiTU5YMSIsIklTTDEiLCJMSFgzIiwiSVNMMiIsICJTTEMxOEEzIikpCgpwcm9nIDwtIGxpc3QoZHAxXzMgPSAgYygiUEFYNiIsIklSWDMiLCJJUlg1IiwiTVNYMSIsIlBBWDMiLCJQQVg3IiksCiAgZHA0ID0gYygiUEFYNiIsIklSWDMiLCJJUlg1IiwiR1NYMSIsIlBBWDMiLCJQQVg3IiksCiAgZHA1ID0gYygiUEFYNiIsIklSWDMiLCJJUlg1IiwiREJYMiIsIkdTWDEiLCJQQVgzIiwiUEFYNyIpLAogIGRwNiA9IGMoIlBBWDYiLCJJUlgzIiwiSVJYNSIsIkRCWDIiLCJMRVVUWCIsIlBBWDMiLCJQQVg3IiksCiAgcDAgPSBjKCJQQVg2IiwiSVJYMyIsIklSWDUiLCJEQlgyIiwiTEVVVFgiKSwKICBwMSA9IGMoIlBBWDYiLCJJUlgzIiwiSVJYNSIsIkRCWDIiLCJQUkRNMTIiKSwKICBwMiA9IGMoIlBBWDYiLCJJUlgzIiwiSVJYNSIsIkZPWE40IiwiTktYNi0xIiksCiAgcE1OID0gYygiUEFYNiIsIk9MSUcyIiwiTktYNi0xIiksCiAgcDMgPSBjKCJOS1gyLTgiLCJOS1gyLTIiLCJOS1g2LTEiKSkKCiMgYWxsb3dzIHRvIHVzZSBtRmVhdHVyZVBsb3Qgd2l0aCBsYXBwbHkKZmVhdF9saXN0X3Bsb3QgPC0gZnVuY3Rpb24oeCkgewogcGxvdCA8LSBtb2RwbG90czo6bUZlYXR1cmVQbG90KG15LmludCwgbXkuZmVhdHVyZXMgPSB4LAogICAgICAgICAgICAgICAgICAgICAgIGduYW1lcyA9IGduYW1lcywgc2l6ZSA9IDAuMiwgcmV0dXJuID0gVFJVRSkKIHJldHVybihwbG90KQp9Cgp0c25lX2RpbSA8LSBUU05FUGxvdCgKICBteS5pbnQsCiAgcmVkdWN0aW9uID0gInRzbmUiLAogIGNvbHMgPSByYWluYm93KGxlbmd0aChsZXZlbHMobXkuSUQpKSksCiAgcHQuc2l6ZSA9IDAuMDEsCiAgbGFiZWwuc2l6ZSA9IDEsCiAgbGFiZWwgPSBUUlVFCikgKwogIGdncGxvdDI6OnRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKCnBsb3RzX3Byb2cgPC0gbGFwcGx5KHByb2csIGZlYXRfbGlzdF9wbG90KQpwbG90c19uZXVyIDwtIGxhcHBseShuZXVyb25zLCBmZWF0X2xpc3RfcGxvdCkKCgpwZGYocGFzdGUwKCJ+L3NwaW5hbF9jb3JkX3BhcGVyL2ZpZ3VyZXMvRFZfcHJvZ19kb21haW5fIixteS5pbnRAcHJvamVjdC5uYW1lICwiLnBkZiIpLCB3aWR0aCA9IDcsIGhlaWdodCA9IDUpCiAgZm9yIChqIGluIG5hbWVzKHByb2cpKSB7CiAgICBwbG90c19wcm9nW1tqXV1bWyJ0c25lIl1dIDwtIHRzbmVfZGltCiAgICBncmlkRXh0cmE6OmdyaWQuYXJyYW5nZShncm9icyA9IHBsb3RzX3Byb2dbW2pdXSwgbmNvbCA9IDMsIHRvcCA9IGopCiAgfQpkZXYub2ZmKCkKCnBkZihwYXN0ZTAoIn4vc3BpbmFsX2NvcmRfcGFwZXIvZmlndXJlcy9EVl9uZXVyX2RvbWFpbl8iLG15LmludEBwcm9qZWN0Lm5hbWUgLCIucGRmIiksIHdpZHRoID0gNywgaGVpZ2h0ID0gNSkKICBmb3IgKGogaW4gbmFtZXMobmV1cm9ucykpIHsKICAgIHBsb3RzX25ldXJbW2pdXVtbInRzbmUiXV0gPC0gdHNuZV9kaW0KICAgIGdyaWRFeHRyYTo6Z3JpZC5hcnJhbmdlKGdyb2JzID0gcGxvdHNfbmV1cltbal1dLCBuY29sID0gMywgdG9wID0gaikKICB9CiAgZGV2Lm9mZigpCmBgYAoKIyBERSBhbmFseXNpcwoKSGVyZSB3ZSBkbyB0aGUgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gYW5hbHlzaXMsIGFuZCBlbmQgdXAgd2l0aCB0aGUgbWFya2VyIGdlbmVzIGxpc3RzLgpXZSBjYW4gYWxzbyBzZWUgdGhlIG1hcmtlciBnZW5lIGRvdCBwbG90LCBmb3IgdGhlIHRvcCAyIG1hcmtlciBnZW5lcyBwZXIgY2x1c3RlcgoKYGBge3IgREUsIGZpZy5oZWlnaHQ9MTAsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGVjaG8gPSBUfQoKIyBGaW5kIGFsbCB0aGUgbWFya2VyIGdlbmVzLCB3aXRoIHRoZXNlIHRocmVzaG9sZHMgTUFTVApzZW1hcmtlcnMgPSBGaW5kQWxsTWFya2VycyhteS5pbnQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGZlYXR1cmVzID0gbXkuaW50W1siaW50ZWdyYXRlZCJdXUB2YXIuZmVhdHVyZXMsIAogICAgICAgICAgICAgICAgICAgICAgICAgICBvbmx5LnBvcyA9IFRSVUUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICBtaW4ucGN0ID0gMC4yNSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGxvZ2ZjLnRocmVzaG9sZCA9ICAwLjUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhdGVudC52YXJzID0gYygiQ0MuRGlmZmVyZW5jZS5zZXVyYXQiKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHRlc3QudXNlID0gIk1BU1QiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgYXNzYXkgPSAiUk5BIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybi50aHJlc2ggPSAwLjA1KQoKIyBXZSBvbmx5IGtlZXAgdGhlIHNpZ25pZmljYW50IG9uZXMKc2VtYXJrZXJzIDwtIHNlbWFya2VycyAlPiUKICBmaWx0ZXIocF92YWxfYWRqIDwgMC4wNSkgJT4lIAogIHJlbmFtZShHZW5lLnN0YWJsZS5JRCA9IGdlbmUpICU+JSAKICBsZWZ0X2pvaW4oZ25hbWVzLCBieSA9ICJHZW5lLnN0YWJsZS5JRCIpCgoKIyBUYWtlIG9ubHkgdGhlIHRvcCAxMApzZW1yazEwID0gc2VtYXJrZXJzICU+JSBncm91cF9ieShjbHVzdGVyKSAlPiUgdG9wX24oLTEwLCBwX3ZhbF9hZGopCnNlbXJrMSA9IHNlbWFya2VycyAlPiUgZ3JvdXBfYnkoY2x1c3RlcikgJT4lIHRvcF9uKC0xLCBwX3ZhbF9hZGopCgptb2RwbG90czo6bURvdFBsb3QyKG15LmludCwgCiAgICAgICAgICBmZWF0dXJlcyA9IHVuaXF1ZShzZW1yazEkR2VuZS5zdGFibGUuSUQpLCAKICAgICAgICAgIGNvbHMgPSBjKCJncmV5IiwgImJsYWNrIiksICAKICAgICAgICAgIGduYW1lcyA9IGduYW1lcywgZG90LnNjYWxlID0gNikgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHZqdXN0ID0gMC41LCBoanVzdD0xKSkKCkRvSGVhdG1hcChteS5pbnQsIHNlbXJrMSRHZW5lLnN0YWJsZS5JRCkKYGBgCgojIHNhdmUKCmBgYHtyIHNhdmluZywgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZWNobyA9IFR9CgpzYXZlUkRTKG15LmludCwgcGFzdGUwKCJ+L3NwaW5hbF9jb3JkX3BhcGVyL2RhdGEvIiwgbXkuaW50QHByb2plY3QubmFtZSwgIl9zZXVyYXRfIixmb3JtYXQoU3lzLkRhdGUoKSwgIiVkJW0leSIpLCIucmRzIikpCgp3cml0ZS50YWJsZShzZW1hcmtlcnMsIHNlcCA9ICJcdCIsIHJvdy5uYW1lcyA9IFQsIGNvbC5uYW1lcyA9IFQsCiAgICAgICAgICAgIGZpbGUgPSBwYXN0ZTAoIn4vc3BpbmFsX2NvcmRfcGFwZXIvZGF0YS8iLCBteS5pbnRAcHJvamVjdC5uYW1lLCAiX2Z1bGxERV8iLGZvcm1hdChTeXMuRGF0ZSgpLCAiJWQlbSV5IiksIi50eHQiKSwgcXVvdGUgPSBGKQoKd3JpdGUudGFibGUoc2VtcmsxMCwgc2VwID0gIlx0Iiwgcm93Lm5hbWVzID0gVCwgY29sLm5hbWVzID0gVCwKICAgICAgICAgICAgZmlsZSA9IHBhc3RlMCgifi9zcGluYWxfY29yZF9wYXBlci9kYXRhLyIsIG15LmludEBwcm9qZWN0Lm5hbWUsICJfdG9wMTBERV8iLGZvcm1hdChTeXMuRGF0ZSgpLCAiJWQlbSV5IiksIi50eHQiKSwgcXVvdGUgPSBGKQoKYGBgCgpgYGB7ciBzZXNzaW9uSW5mb30KIyBEYXRlIGFuZCB0aW1lIG9mIFJlbmRlcmluZwpTeXMudGltZSgpCgpzZXNzaW9uSW5mbygpCmBgYAo=